﻿//
// *************************************************************************************
// WP-Framework
// 
// Developed by
// Lukas Kretschmar (lkretschmar.dev@bluewin.ch)
//
// Switzerland, Copyright (c) 2013 lkretschmar
// This content is released under the MIT License (http://opensource.org/licenses/MIT).
//     
// *************************************************************************************
//
//
			

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using KretschIT.WP_Fx.Core.Validation;
using KretschIT.WP_Fx.EasyMoq.Generator.Items;

namespace KretschIT.WP_Fx.EasyMoq.Generator.Configurations
{
    public class EasyMoqClassConfigReader : IEasyMoqConfigReader
    {
        private Assembly assembly;

        public EasyMoqClassConfigReader(Assembly assembly)
        {
            It.IsNotNull(() => assembly);

            this.assembly = assembly;
        }

        public string TargetNamespace
        {
            get { return this.Config.TargetNamespace; }
        }

        public IEnumerable<IMockableInfo> GetMockableInfo()
        {
            var mockables = new List<IMockableInfo>();
            foreach (var t in this.Config.MockTypes)
            {
                this.Handle(t, mockables, this.NameFactory);
            }
            return mockables;
        }

        private void Handle(Type t, List<IMockableInfo> mockables, INamingFactory nameFactory)
        {
            var mockable = mockables.SingleOrDefault(m => m.Represents(t));
            if (mockable == null)
            {
                this.AddNewMockableInfoFor(t, mockables, nameFactory);
            }
            else
            {
                this.UpdateMockableInfoFor(t, mockable);
            }
        }

        private void AddNewMockableInfoFor(Type t, List<IMockableInfo> mockables, INamingFactory nameFactory)
        {
            mockables.Add(new MockableInfo(t, nameFactory));
        }
        
        private void UpdateMockableInfoFor(Type t, IMockableInfo mockable)
        {
            if (t.IsGenericType)
            {
                mockable.AddGenericTypes(t.GetGenericArguments());
            }
        }

        private IEasyMoqInternalConfig config;

        private IEasyMoqInternalConfig Config
        {
            get
            {
                if (this.config == null)
                {
                    this.config = this.LoadConfig();
                }
                return this.config;
            }
        }

        private IEasyMoqInternalConfig LoadConfig()
        {
            var generators = this.GetGenerators();
            if (!generators.Any())
            {
                return new EasyMoqConfig();                
            }
            return this.LoadConfigFrom(generators);
        }

        private IEasyMoqInternalConfig LoadConfigFrom(IEnumerable<Type> generators)
        {
            var config = new EasyMoqConfig();
            foreach (var type in generators)
            {
                var generator = (EasyMoqGenerator)Activator.CreateInstance(type);
                generator.LoadConfiguration(config);
            }
            return config;
        }

        private IEnumerable<Type> GetGenerators()
        {
            return this.GetAssemblyTypes()
                .Where(t => t.IsDefined(typeof(EasyMoqGeneratorAttribute), false) && t.IsSubclassOf(typeof(EasyMoqGenerator)));

        }

        private IEnumerable<Type> GetAssemblyTypes()
        {
            try
            {
                return this.assembly.GetTypes().ToList();
            }
            catch (ReflectionTypeLoadException ex)
            {
                return ex.Types.Where(t => t != null);
            }
        }

        private INamingFactory nameFactory;

        public INamingFactory NameFactory
        {
            get
            {
                if (this.nameFactory == null)
                {
                    this.nameFactory = new NamingFactory(this.Config.MockTypes);
                }
                return this.nameFactory;
            }
        }
    }
}
